using LightCAD.MathLib;
using System;
using System.Collections;
using System.Collections.Generic;


namespace LightCAD.Three
{
    public class WireframeGeometry : BufferGeometry
    {
        public class Parameters
        {
            public BufferGeometry geometry;
        }
        #region scope properties or methods

        private static bool isUniqueEdge(Vector3 start, Vector3 end, ListEx<string> edges)
        {

            string hash1 = $"{start.X},{start.Y},{start.Z}-{end.X},{end.Y},${end.Z}";
            string hash2 = $"{end.X},{end.Y},{end.Z}-{start.X},{start.Y},{start.Z}"; // coincident edge
            if (edges.IndexOf(hash1) > -1 || edges.IndexOf(hash2) > -1)
            {
                return false;
            }
            else
            {
                edges.Push(hash1);
                edges.Push(hash2);
                return true;
            }
        }
        #endregion

        #region Properties

        public Parameters parameters;

        #endregion

        #region constructor
        public WireframeGeometry(BufferGeometry geometry = null) : base()
        {
            this.type = "WireframeGeometry";
            this.parameters = new Parameters
            {
                geometry = geometry
            };
            if (geometry != null)
            {
                // buffer
                var vertices = new ListEx<double>();
                var edges = new ListEx<string>();
                // helper variables
                var start = new Vector3();
                var end = new Vector3();
                if (geometry.index != null)
                {
                    // indexed BufferGeometry
                    var position = geometry.attributes.position;
                    var indices = geometry.index;
                    var groups = geometry.groups;
                    if (groups.Length == 0)
                    {
                        groups = new ListEx<GeometryGroup> { new GeometryGroup { Start = 0, Count = indices.count, MaterialIndex = 0 } };
                    }
                    // create a data structure that contains all edges without duplicates
                    for (int o = 0, ol = groups.Length; o < ol; ++o)
                    {
                        var group = groups[o];
                        var groupStart = group.Start;
                        var groupCount = group.Count;
                        for (int i = groupStart, l = (groupStart + groupCount); i < l; i += 3)
                        {
                            for (int j = 0; j < 3; j++)
                            {
                                var index1 = indices.getIntX(i + j);
                                var index2 = indices.getIntX(i + (j + 1) % 3);
                                start.FromBufferAttribute(position, index1);
                                end.FromBufferAttribute(position, index2);
                                if (isUniqueEdge(start, end, edges))
                                {
                                    vertices.Push(start.X, start.Y, start.Z);
                                    vertices.Push(end.X, end.Y, end.Z);
                                }
                            }
                        }
                    }
                }
                else
                {
                    // non-indexed BufferGeometry
                    var position = geometry.attributes.position;
                    for (int i = 0, l = (position.count / 3); i < l; i++)
                    {
                        for (int j = 0; j < 3; j++)
                        {
                            // three edges per triangle, an edge is represented as (index1, index2)
                            // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0)
                            var index1 = 3 * i + j;
                            var index2 = 3 * i + ((j + 1) % 3);
                            start.FromBufferAttribute(position, index1);
                            end.FromBufferAttribute(position, index2);
                            if (isUniqueEdge(start, end, edges))
                            {
                                vertices.Push(start.X, start.Y, start.Z);
                                vertices.Push(end.X, end.Y, end.Z);
                            }
                        }
                    }
                }
                // build geometry
                this.setAttribute("position", new Float32BufferAttribute(vertices.ToArray(), 3));
            }
        }
        #endregion

        #region methods
        public WireframeGeometry copy(WireframeGeometry source)
        {
            base.copy(source);
            this.parameters = new Parameters()
            {
                geometry = source.parameters.geometry,
            };
            return this;
        }
        #endregion
    }
}
